home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 June: Reference Library / Dev.CD Jun 95 / Dev.CD Jun 95.toast / Technical Documentation / PCI Information / PCI Developer’s Kit (disk) / Open Firmware / SCSI example / scsiha.of < prev    next >
Encoding:
Text File  |  1994-06-27  |  7.0 KB  |  201 lines  |  [TEXT/EDIT]

  1. \ Example FCode driver for a hypothetical SCSI bus interface device
  2.  
  3. hex
  4.  
  5. \ The following structure defines the registers for the SCSI device.
  6. \ This hypothetical device is designed for ease of programming.  It
  7. \ has a separate register for each function (no bit packing).  All
  8. \ registers are both readable and writeable.  The device has a random-
  9. \ access buffer large enough for a maximum-length SCSI command block.
  10.  
  11. \ To execute a SCSI command with this device, write the appropriate
  12. \ information into the registers named ">cmd-adr" through ">input", write
  13. \ a 1 to the ">start" register, and wait for the ">start" register to
  14. \ change to 0.  Then read the ">phase" register to determine whether or
  15. \ not the command completed all phases (">phase" reports 0 on success,
  16. \ h# fd for incoming reset, h# ff for other hardware error).
  17. \ If so, ">status" contains the SCSI status byte, and ">message-in"
  18. \ contains the command-complete message byte.
  19.  
  20. struct  ( scsi-registers )
  21.   0c field >cmd-adr               \ Up to 12 command bytes 
  22.    4 field >cmd-len               \ Length of command block
  23.  
  24.    4 field >data-adr              \ Base address of DMA data area
  25.    4 field >data-len              \ Length of data area
  26.  
  27.    1 field >host-selectid         \ Host's selection ID
  28.    1 field >target-selectid       \ Target's selection ID
  29.    1 field >input?                \ 1 for data output, 0 for data input
  30.    1 field >message-out           \ Outgoing message byte
  31.  
  32.    1 field >start                 \ Write 1 to start.  Reads as 0 when done.
  33.    1 field >phase                 \ Reports the last transaction phase 
  34.    1 field >status                \ Returned status byte
  35.    1 field >message-in            \ Incoming message byte
  36.  
  37.    1 field >intena                \ Write 1 to enable interrupts.
  38.    1 field >reset-bus             \ Write 1 to reset the SCSI bus
  39.    1 field >reset-board           \ Write 1 to reset the board
  40. constant /scsi-regs
  41.  
  42. 1 constant bus-reset    \ Hardware result code that denotes an incoming
  43.             \ SCSI bus reset
  44.  
  45. \ Now that we have a symbolic name for the size of the register block,
  46. \ we can declare the "reg" property
  47.  
  48. \ Registers begin at offset 800000 and continue for "/scsi-regs" bytes
  49.  
  50. my-address 80.0000 +  my-space  /scsi-regs  reg
  51.  
  52.  
  53. -1 instance value regs            \ Virtual base address of device registers
  54.    
  55. 0 instance value my-id            \ host adapter's selection ID
  56. 0 instance value his-id           \ target's selection ID
  57. 0 instance value his-lun          \ target's unit number
  58.  
  59. \ Map device registers
  60.  
  61. : map  ( -- )
  62.    my-address 80.0000 +  my-space  /scsi-regs  ( addr-low addr-high size )
  63.    " map-in" $call-parent   is regs            ( )
  64. ;
  65. : unmap  ( -- )
  66.    regs /scsi-regs  " map-out" $call-parent  -1 is regs
  67. ;
  68.  
  69. create reset-done-time 0 ,
  70. create resetting false ,
  71.  
  72. \ 5 seconds appears to be about the right length of time to wait after
  73. \ a reset, considering a variety of disparate devices.
  74. d# 5000 value scsi-reset-delay
  75.  
  76. : reset-wait  ( -- )
  77.    resetting @  if
  78.       begin  get-msecs reset-done-time @ -  0>=  until
  79.       resetting off
  80.    then
  81. ;
  82.  
  83. : reset-scsi-bus  ( -- )
  84.    1 regs >reset-board rb!        \ Reset the controller board
  85.    0 regs >intena      rb!        \ Turn off interrupts
  86.    1 regs >reset-bus   rb!        \ Reset the SCSI bus
  87.  
  88.    \ After resetting the SCSI bus, we have to give the target devices
  89.    \ some time to initialize their microcode.  Otherwise the first command
  90.    \ may hang, as with some older controllers.  We note the time when it
  91.    \ is okay to access the bus (now plus some delay), and "execute-command"
  92.    \ will delay until that time is reached, if necessary.
  93.    \ This allows us to overlap the delay with other work in many cases.
  94.  
  95.    get-msecs scsi-reset-delay + reset-done-time !  resetting on
  96. ;
  97.  
  98. 0 value scsi-time       \ Maximum command time in milliseconds
  99. 0 value time-limit      \ Ending time for command
  100.  
  101. 0 value devaddr
  102.  
  103. \ Returns true if select failed
  104. : (exec)  ( dma-adr,len dir cmd-adr,len -- hwresult )
  105.    reset-wait           \ Delay until any prior reset operation is done
  106.  
  107.    his-lun h# 80 or  regs >message-out rb! \ Set the unit number, no disconnect
  108.    my-id      regs >host-selectid   rb!    \ Set the selection IDs
  109.    his-id     regs >target-selectid rb!
  110.  
  111.    \ Write the command block into the host adapter's command register
  112.  
  113.    dup 0  ?do                       ( data-adr,len dir cmd-adr,len )
  114.       over i + c@                   ( data-adr,len dir cmd-adr,len cmd-byte )
  115.       regs >cmd-adr i ca+ rb!       ( data-adr,len dir cmd-adr,len )
  116.    loop                             ( data-adr,len dir cmd-adr,len )
  117.  
  118.    regs >cmd-len rl!   drop         ( data-adr,len dir )
  119.  
  120.  
  121.    \ Set the data transfer parameters
  122.  
  123.    ( .. dir ) regs >input?   rb!    ( data-adr,len )  \ Direction
  124.    ( .. len ) regs >data-len rl!    ( data-adr )      \ Length
  125.    ( .. adr ) regs >data-adr rl!    ( )               \ DMA Address
  126.  
  127.    \ Now we're ready to execute the command
  128.  
  129.    1  regs >start  rb!                    \ Tell board to start the command
  130.  
  131.    get-msecs scsi-time +  is time-limit   \ Set the time limit
  132.  
  133.    begin  regs >start rb@  while          \ Wait until command finished
  134.  
  135.       scsi-time  if                       \ If timeout is enabled, and
  136.          get-msecs time-limit -  0>=  if  \ the time-limit has been reached,
  137.             reset-scsi-bus  true  exit    \ reset the bus and return error
  138.          then
  139.       then
  140.  
  141.    repeat
  142.  
  143.    \ Nonzero phase means that the command didn't finish
  144.  
  145.    regs >phase rb@
  146. ;
  147.  
  148. \ Returns true if select failed
  149. : execute-command ( data-adr,len dir cmd-adr,len -- hwresult | statbyte false )
  150.    \ Put dir and cmd-adr,len on the return stack temporarily, to get them
  151.    \ out of the way so we can work on the DMA data buffer.
  152.  
  153.    >r >r >r                     ( data-adr,len )
  154.  
  155.    dup  if                      ( data-adr,len )
  156.  
  157.       \ If the data transfer has a non-zero length, we have to map it in
  158.  
  159.       2dup  false  dma-map-in   ( data-adr,len dma )
  160.       2dup swap  r> r> r>       ( data-adr,len dma dma,len dir cmd-adr,len)
  161.  
  162.       (exec)                    ( data-adr,len phys hwres)
  163.  
  164.       >r swap dma-map-out  r>   ( hwresult )
  165.    else                         ( data-adr,len )
  166.       r> r> r>  (exec)          ( hwresult )
  167.    then                         ( hwresult )
  168.  
  169.    ?dup  0=  if                                    ( hwresult | )
  170.       regs >status rb@  false  \ Command finished; return status byte and false
  171.    then                                            ( hwresult | statbyte 0 )
  172. ;
  173.  
  174. external
  175.  
  176. : reset  ( -- )  map  reset-scsi-bus  unmap  ;
  177. reset   \ Reset the SCSI bus when we are probed.
  178.  
  179. : open-hardware  ( -- flag )
  180.    map
  181.  
  182.    \ should perform a quick "sanity check" selftest here,
  183.    \ returning true iff the test succeeds.
  184.  
  185.    true
  186. ;
  187. : reopen-hardware  ( -- flag )  true  ;
  188.  
  189. : close-hardware  ( -- )  unmap  ;
  190. : reclose-hardware  ( -- )  ;
  191.  
  192. : selftest  ( -- 0 | error-code )
  193.    \ perform reasonably extensive selftest here, displaying
  194.    \ a message if the test fails, and returning an error code if the
  195.    \ test fails or 0 if the test succeeds.
  196.    0
  197. ;
  198. : set-address  ( unit target -- )
  199.    is his-id  is his-lun
  200. ;
  201.